﻿using Microsoft.EntityFrameworkCore;
using SD.Infrastructure.Repository.EntityFrameworkCore;
using SlamDunk.MES.Domain.Entities.PlanContxt;
using SlamDunk.MES.Domain.Entities.QualityContext;
using SlamDunk.MES.Domain.Entities.ResourceContext;
using SlamDunk.MES.Domain.IRepositories.Interfaces.QualityContext;
using SlamDunk.MES.ReportView.Models;
using SlamDunk.ValueObjects.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SlamDunk.MES.Repository.Implements.QualityContext
{
    /// <summary>
    /// 质量检查仓储实现
    /// </summary>
    public class QualityInspectionRepository : EFAggRootRepositoryProvider<QualityInspection>, IQualityInspectionRepository
    {
        #region # 分页获取作业计划质量检查列表 —— ICollection<ExecutionPlanInspection> FindExecutionPlanInspectionsByPage(...
        /// <summary>
        /// 分页获取作业计划质量检查列表
        /// </summary>
        /// <param name="keywords">关键字</param>
        /// <param name="executionPlanId">作业计划Id</param>
        /// <param name="checkStatus">审核状态</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="rowCount">总记录数</param>
        /// <param name="pageCount">总页数</param>
        /// <returns>作业计划质量检查列表</returns>
        public ICollection<ExecutionPlanInspection> FindExecutionPlanInspectionsByPage(string keywords, Guid? executionPlanId, CheckStatus? checkStatus, DateTime? startTime, DateTime? endTime, int pageIndex, int pageSize, out int rowCount, out int pageCount)
        {
            Expression<Func<ExecutionPlanInspection, bool>> condition =
                x =>
                    (string.IsNullOrEmpty(keywords) || x.Keywords.Contains(keywords)) &&
                    (executionPlanId == null || x.ExecutionPlanId == executionPlanId) &&
                    (checkStatus == null || x.CheckStatus == checkStatus) &&
                    (startTime == null || x.InspectedDate >= startTime) &&
                    (endTime == null || x.InspectedDate <= endTime);

            IQueryable<ExecutionPlanInspection> inspections = base.FindByPage(condition, pageIndex, pageSize, out rowCount, out pageCount);

            return inspections.ToList();
        }
        #endregion

        #region # 分页获取工单质量检查列表 —— ICollection<WorkOrderInspection> FindWorkOrderInspectionsByPage(...
        /// <summary>
        /// 分页获取工单质量检查列表
        /// </summary>
        /// <param name="keywords">关键字</param>
        /// <param name="executionPlanId">作业计划Id</param>
        /// <param name="workOrderId">工单Id</param>
        /// <param name="checkStatus">审核状态</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="rowCount">总记录数</param>
        /// <param name="pageCount">总页数</param>
        /// <returns>工单质量检查列表</returns>
        public ICollection<WorkOrderInspection> FindWorkOrderInspectionsByPage(string keywords, Guid? executionPlanId, Guid? workOrderId, CheckStatus? checkStatus, DateTime? startTime, DateTime? endTime, int pageIndex, int pageSize, out int rowCount, out int pageCount)
        {
            Expression<Func<WorkOrderInspection, bool>> condition =
                x =>
                    (string.IsNullOrEmpty(keywords) || x.Keywords.Contains(keywords)) &&
                    (executionPlanId == null || x.ExecutionPlanId == executionPlanId) &&
                    (workOrderId == null || x.WorkOrderId == workOrderId) &&
                    (checkStatus == null || x.CheckStatus == checkStatus) &&
                    (startTime == null || x.InspectedDate >= startTime) &&
                    (endTime == null || x.InspectedDate <= endTime);

            IQueryable<WorkOrderInspection> inspections = base.FindByPage(condition, pageIndex, pageSize, out rowCount, out pageCount);

            return inspections.ToList();
        }
        #endregion

        #region # 获取产品合格率字典 —— IDictionary<Guid, decimal> GetProductQualifiedRates(DateTime inspectedDate)
        /// <summary>
        /// 获取产品合格率字典
        /// </summary>
        /// <param name="inspectedDate">质检日期</param>
        /// <returns>产品合格率字典</returns>
        public IDictionary<Guid, decimal> GetProductQualifiedRates(DateTime inspectedDate)
        {
            inspectedDate = inspectedDate.Date;
            IQueryable<QualityInspection> inspections = base.Find(x => x.InspectedDate == inspectedDate).Include(x => x.Details);
            IQueryable<QualityInspectionDetail> details = inspections.SelectMany(x => x.Details);
            var detailGroups = details.GroupBy(x => x.ProductId).Select(x => new
            {
                x.Key,
                ProductionQuantity = x.Any() ? x.Sum(y => y.ProductionQuantity) : 0,
                QualifiedQuantity = x.Any() ? x.Sum(y => y.QualifiedQuantity) : 0
            });

            var result =
                from detailGroup in detailGroups
                let productId = detailGroup.Key
                let totalProductionQuantity = detailGroup.ProductionQuantity
                let totalQualifiedQuantity = detailGroup.QualifiedQuantity
                let qualifiedRate = totalProductionQuantity == 0m ? 0 : totalQualifiedQuantity / totalProductionQuantity
                select new
                {
                    Key = productId,
                    Value = qualifiedRate
                };

            IDictionary<Guid, decimal> dictionary = result.ToDictionary(x => x.Key, x => x.Value);

            return dictionary;
        }
        #endregion

        #region # 获取产品产出周期概要字典 —— {DateTime, ProductQuantityInfo[]} GetProductOutputPeriodicSummaries(...
        /// <summary>
        /// 获取产品产出周期概要字典
        /// </summary>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>产品产出周期概要字典</returns>
        /// <remarks>
        /// 键：日期，值：产品数量列表
        /// </remarks>
        public IDictionary<DateTime, ProductQuantityInfo[]> GetProductOutputPeriodicSummaries(DateTime startTime, DateTime endTime)
        {
            Expression<Func<ExecutionPlanInspection, bool>> condition =
                x =>
                    x.InspectedDate >= startTime &&
                    x.InspectedDate <= endTime;
            IQueryable<ExecutionPlanInspection> inspections = base.Find(condition).Include(x => x.Details);
            IQueryable<QualityInspectionDetail> details = inspections.SelectMany(x => x.Details);

            IQueryable<Guid> productIds = details.Select(x => x.ProductId);
            IQueryable<Guid> executionPlanIds = inspections.Select(x => x.ExecutionPlanId);
            IDictionary<Guid, Material> materials = base._dbContext.Set<Material>().Where(x => productIds.Contains(x.Id)).ToDictionary(x => x.Id, x => x);
            IDictionary<Guid, ExecutionPlan> executionPlans = base._dbContext.Set<ExecutionPlan>().Where(x => executionPlanIds.Contains(x.Id)).ToDictionary(x => x.Id, x => x);

            IDictionary<DateTime, ProductQuantityInfo[]> dictionary = new Dictionary<DateTime, ProductQuantityInfo[]>();

            var detailDatedGroups = details.AsEnumerable().GroupBy(x => x.QualityInspection.InspectedDate).ToList();
            foreach (var detailDatedGroup in detailDatedGroups)
            {
                var detailGroups = detailDatedGroup.GroupBy(x => new
                {
                    x.ProductId,
                    x.QualityInspection
                }).ToList();

                IList<ProductQuantityInfo> productQuantities = new List<ProductQuantityInfo>();
                foreach (var detailGroup in detailGroups)
                {
                    Material product = materials[detailGroup.Key.ProductId];
                    ExecutionPlanInspection executionPlanInspection = (ExecutionPlanInspection)detailGroup.Key.QualityInspection;
                    ExecutionPlan executionPlan = executionPlans[executionPlanInspection.ExecutionPlanId];
                    decimal productionQuantity = detailGroup.Sum(x => x.ProductionQuantity);
                    decimal qualifiedQuantity = detailGroup.Sum(x => x.QualifiedQuantity);
                    decimal sampleQuantity = detailGroup.Sum(x => x.SampleQuantity);

                    ProductQuantityInfo productQuantityInfo = new ProductQuantityInfo
                    {
                        ProductId = product.Id,
                        ProductNo = product.Number,
                        ProductName = product.Name,
                        ExpGrossQuantity = executionPlan.ExpGrossQuantity,
                        ExpNetQuantity = executionPlan.ExpNetQuantity,
                        ProductionQuantity = productionQuantity,
                        QualifiedQuantity = qualifiedQuantity,
                        SampleQuantity = sampleQuantity,
                        QuantityUnitNo = product.StandardUnitNo
                    };
                    productQuantities.Add(productQuantityInfo);
                }

                dictionary.Add(detailDatedGroup.Key, productQuantities.ToArray());
            }

            return dictionary;
        }
        #endregion

        #region # 获取产品数量列表 —— ICollection<ProductQuantityInfo> GetProductQuantities(DateTime startTime...
        /// <summary>
        /// 获取产品数量列表
        /// </summary>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>产品数量列表</returns>
        public ICollection<ProductQuantityInfo> GetProductQuantities(DateTime startTime, DateTime endTime)
        {
            Expression<Func<ExecutionPlanInspection, bool>> condition =
                x =>
                    x.InspectedDate >= startTime &&
                    x.InspectedDate <= endTime;
            IQueryable<ExecutionPlanInspection> inspections = base.Find(condition).Include(x => x.Details);
            IQueryable<QualityInspectionDetail> details = inspections.SelectMany(x => x.Details);

            IQueryable<Guid> productIds = details.Select(x => x.ProductId);
            IQueryable<Guid> executionPlanIds = inspections.Select(x => x.ExecutionPlanId);
            IDictionary<Guid, Material> materials = base._dbContext.Set<Material>().Where(x => productIds.Contains(x.Id)).ToDictionary(x => x.Id, x => x);
            IDictionary<Guid, ExecutionPlan> executionPlans = base._dbContext.Set<ExecutionPlan>().Where(x => executionPlanIds.Contains(x.Id)).ToDictionary(x => x.Id, x => x);

            var detailGroups = details.AsEnumerable().GroupBy(x => new
            {
                x.ProductId,
                x.QualityInspection
            }).ToList();
            IList<ProductQuantityInfo> productQuantities = new List<ProductQuantityInfo>();
            foreach (var detailGroup in detailGroups)
            {
                Material product = materials[detailGroup.Key.ProductId];
                ExecutionPlanInspection executionPlanInspection = (ExecutionPlanInspection)detailGroup.Key.QualityInspection;
                ExecutionPlan executionPlan = executionPlans[executionPlanInspection.ExecutionPlanId];
                decimal productionQuantity = detailGroup.Sum(x => x.ProductionQuantity);
                decimal qualifiedQuantity = detailGroup.Sum(x => x.QualifiedQuantity);
                decimal sampleQuantity = detailGroup.Sum(x => x.SampleQuantity);

                ProductQuantityInfo productQuantityInfo = new ProductQuantityInfo
                {
                    ProductId = product.Id,
                    ProductNo = product.Number,
                    ProductName = product.Name,
                    ExpGrossQuantity = executionPlan.ExpGrossQuantity,
                    ExpNetQuantity = executionPlan.ExpNetQuantity,
                    ProductionQuantity = productionQuantity,
                    QualifiedQuantity = qualifiedQuantity,
                    SampleQuantity = sampleQuantity,
                    QuantityUnitNo = product.StandardUnitNo
                };
                productQuantities.Add(productQuantityInfo);
            }

            return productQuantities;
        }
        #endregion
    }
}
